/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package slicktemplate;

import java.awt.geom.Point2D;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Random;
import java.util.StringTokenizer;
import org.newdawn.slick.*;
import org.newdawn.slick.state.BasicGameState;
import org.newdawn.slick.state.StateBasedGame;

/**
 *
 * @author andrew
 */
public class Level1 extends BasicGameState {

    //These lists hold the lists of types of objects, which are metaphysical and kept in the class
    static ArrayList<ArrayList> renderList = new ArrayList<ArrayList>();
    static ArrayList<ArrayList> updateList = new ArrayList<ArrayList>();
    static ArrayList<ArrayList> placeOverList = new ArrayList<ArrayList>();
    static ArrayList<ArrayList> enemyBlockers = new ArrayList<ArrayList>();
    static ArrayList<ArrayList> turrets = new ArrayList<ArrayList>();
    int stateId = -1;
    boolean drawGrid = false;
    static int mousePressed = -1;
    int mouseDown = -1;
    Point2D.Float currentTLPosition;
    LinkedList currentTimeLine;
    Collideable[][] grid;
    long steps = 0;
    Sound levelSound;
    Image bg;

    Level1(int stateId) {
        this.stateId = stateId; 
        try {
            levelSound = new Sound("sounds/track2.ogg");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public int getID() {
        return stateId;
    }

    @Override
    public void leave(GameContainer container, StateBasedGame game) throws SlickException {
        super.leave(container, game);
        levelSound.stop();
    }

    @Override
    public void enter(GameContainer container, StateBasedGame game) throws SlickException {
        super.enter(container, game);
        levelSound.loop();
    }
    
    

    @Override
    public void init(GameContainer container, StateBasedGame game) throws SlickException {
        System.out.println("Init called level1");
        try {
            bg = new Image("sprites/bg.png");
        } catch (Exception e) {
        }
        
        //Adding to renderList
        // renderList.add(Player.all); //add the lists of players to the list of renderable lists
        renderList.add(BuildGui.all);
        renderList.add(Wall.all);
        renderList.add(WallButton.all);
        renderList.add(Enemy1.all);
        renderList.add(WallPlacer.all);
        renderList.add(TurretPlacer.all);
        renderList.add(TurretButton.all);
        renderList.add(Turret.all);
        renderList.add(ProjectileNail.all);
        renderList.add(ProjectileDynamite.all);
        renderList.add(TurretDynamiteButton.all);
        renderList.add(TurretDynamitePlacer.all);
        renderList.add(TurretDynamite.all);
        renderList.add(BasicBuilding.all);
        renderList.add(ProjectileSlow.all);
        renderList.add(TurretSlowButton.all);
        renderList.add(TurretSlowPlacer.all);
        renderList.add(TurretSlow.all);
        renderList.add(Base.all);
        for (int i = 0; i < renderList.size(); i++) {
            renderList.get(i).clear();
        }
        //Adding to updateList
        // updateList.add(Player.all);
        updateList.add(WallButton.all);
        updateList.add(Enemy1.all);
        updateList.add(WallPlacer.all);
        updateList.add(TurretPlacer.all);
        updateList.add(TurretButton.all);
        updateList.add(Turret.all);
        updateList.add(ProjectileNail.all);
        updateList.add(ProjectileDynamite.all);
        updateList.add(TurretDynamiteButton.all);
        updateList.add(TurretDynamitePlacer.all);
        updateList.add(TurretDynamite.all);
        updateList.add(Wall.all);
        updateList.add(ProjectileSlow.all);
        updateList.add(TurretSlow.all);
        updateList.add(TurretSlowButton.all);
        updateList.add(TurretSlowPlacer.all);
        updateList.add(BasicBuilding.all);
        updateList.add(Base.all);
        for (int i = 0; i < updateList.size(); i++) {
            updateList.get(i).clear();
        }


        placeOverList.add(Wall.all);
        placeOverList.add(Turret.all);
        placeOverList.add(BuildGui.all);
        placeOverList.add(TurretDynamite.all);
        placeOverList.add(ProjectileSlow.all);
        placeOverList.add(TurretSlow.all);
        placeOverList.add(Base.all);

        enemyBlockers.add(Wall.all);
        enemyBlockers.add(Turret.all);
        enemyBlockers.add(TurretDynamite.all);
        enemyBlockers.add(Enemy1.all);
        enemyBlockers.add(BasicBuilding.all);
        enemyBlockers.add(TurretSlow.all);
        enemyBlockers.add(Base.all);
        
        turrets.add(Turret.all);
        turrets.add(TurretSlow.all);
        turrets.add(TurretDynamite.all);
        turrets.add(Base.all);
        
        
        currentTimeLine = loadSpawnTimeLine("TLL1.txt");
        grid = new Collideable[Game.height / 32][Game.height / 32];
        new BuildGui();
        new WallButton(Game.width - Game.width / 5, 50);
        new TurretButton(Game.width - Game.width / 5, 100);
        new TurretDynamiteButton(Game.width - Game.width / 5, 150);
        new TurretSlowButton(Game.width - Game.width / 5, 200);
        grid[Game.height/64][Game.height/64] = new Base(Game.height/2,Game.height/2);
        
    }

    @Override
    public void render(GameContainer container, StateBasedGame game, Graphics g) throws SlickException {
        if(bg == null) {
            System.out.println("bob");
        }
        bg.draw(0, 0);
        if (drawGrid == true) {
            for (int i = 0; i < Game.width; i += 32) {
                g.drawLine(i, 0, i, Game.height);
            }
            for (int i = 0; i < Game.height; i += 32) {
                g.drawLine(0, i, Game.width, i);
            }
        }
        for (ArrayList<Renderable> it : renderList) { //go through all the objects and draw them
            for (Renderable obj : it) {
                obj.render(g);
            }
        }
        g.drawString("Money:" + Game.money, Game.width - Game.width / 7, 3);

    }

    @Override
    public void update(GameContainer container, StateBasedGame game, int delta) throws SlickException {
        Input input = container.getInput();
        if (input.isKeyPressed(input.KEY_V)) {
            if (drawGrid == false) {
                drawGrid = true;
            } else {
                drawGrid = false;
            }
        }
        if (input.isKeyPressed(Input.KEY_ESCAPE)) {//A much needed way to exit the game
            container.exit();
        }
        if (input.isMousePressed(Input.MOUSE_MIDDLE_BUTTON)) {//A much needed way to exit the game
            spawnEnemy();
        }
        if (currentTLPosition == null) {
            currentTLPosition = (Point2D.Float) currentTimeLine.poll();
        }
        if (currentTLPosition != null) {
            if (currentTLPosition.x == steps) {
                for (int i = 0; i < currentTLPosition.y; i++) {
                    spawnEnemy();
                }
                currentTLPosition = null;
            }
        }

        if (steps % 16 == 0) {
            decideMovements();
        }

        steps++;

        for (ArrayList<Updateable> it : updateList) { //go through all objects and update them
            for (int i = 0; i < it.size(); i++) {
                Updateable obj = it.get(i);
                if (obj.update(input, game)) {
                    it.remove(obj);
                    if(obj.getClass().getSuperclass() == Collideable.class) {
                        for (int j = 0; j < grid.length; j++) {
                            for (int k = 0; k < grid.length; k++) {
                                if(obj.equals(grid[j][k])) {
                                    grid[j][k] = null;
                                }
                            }
                        }
                    } 
                    i--;
                }
            }
        }
    }

    public void spawnEnemy(float x, float y) {
        new Enemy1(x, y);
    }

    public void spawnEnemy() {
        Random rand = new Random();
        int x = 0;
        int y = 0;
        int side = rand.nextInt(4);
        switch (side) {
            case 0:
                x = 0;
                y = rand.nextInt(Game.height / 32);
                break;
            case 1:
                x = Game.height / 32 - 1;
                y = rand.nextInt(Game.height / 32);
                break;
            case 2:
                y = 0;
                x = rand.nextInt(Game.height / 32);
                break;
            case 3:
                y = Game.height / 32 - 1;
                x = rand.nextInt(Game.height / 32);
                break;
        }
        grid[x][y] = new Enemy1(x * 32, y * 32);
    }

    private void decideMovements() {
        for (Enemy1 en : Enemy1.all) {
            Attackable temp = en.getNearestTurret(turrets);
            if (temp == null) {
                en.state = Enemy1.NULL;  
            } else {
                int goalx = (int) en.getX();
                int goaly = (int) en.getY();
                int x = goalx;
                int y = goaly;
                int dx = (int) (temp.getX() - x);
                int dy = (int) (temp.getY() - y);
                int movex = x;
                int movey = y;


                en.state = en.MOVE;
                if (Math.abs(dx) > 0) {
                    goalx = (int) (Integer.signum(dx) * 32 + en.getX());
                    if (grid[goalx / 32][goaly / 32] != null) {
                        movex = x;
                    } else {
                        movex = goalx;
                    }
                }
                
                if (Math.abs(dy) > 0) {
                    goaly = (int) (Integer.signum(dy) * 32 + en.getY());
                    if (grid[goalx / 32][goaly / 32] != null) {
                        movey = y;
                    } else {
                        movey = goaly;
                    }
                }
                
                if (x == movex && y == movey) {
                    if (goalx == x) {
                        Collideable target = grid[x / 32][goaly / 32];
                        if (target != null && target.isBuilding()) {
                            en.state = en.ATTACK; 
                            en.target = (Attackable) target;
                        }
                    } else {
                        Collideable target = grid[goalx / 32][y / 32];
                        if (target != null && target.isBuilding()) {
                            en.state = en.ATTACK;
                            en.target = (Attackable) target;
                        }
                    }
                }
                grid[x / 32][y / 32] = null;
                grid[movex / 32][movey / 32] = en;
                en.setTargetPoint(movex, movey);
            }
        }
    }

    public void addToGrid(Collideable it, int x, int y) {
        grid[x / 32][y / 32] = it;
    }


    /*
     * Loads a timeline from a file:
     * Time will be measured in steps whichever is more convinient
     *
     *
     */
    public LinkedList loadSpawnTimeLine(String file) {
        LinkedList ll = new LinkedList();
        try {
            BufferedReader br = new BufferedReader(new FileReader(new File(file)));
            String input;
            while ((input = br.readLine()) != null) {
                for (StringTokenizer st = new StringTokenizer(input, " "); st.hasMoreTokens();) {
                    String token = st.nextToken();
                    int colon = token.indexOf(":");
                    int time = Integer.parseInt(token.substring(0, colon));
                    int amount = Integer.parseInt(token.substring(colon + 1, token.length()));
                    Point2D.Float point = new Point2D.Float(time, amount);
                    ll.add(point);
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

        return ll;

    }
}
